package org.jeik.data.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;

import org.jeik.common.Constants;
import org.jeik.utils.Configs;
import org.jeik.utils.Log;

/**
 * 
 * @author AAsha
 *
 */
public abstract class ConnectionManager {
	
	private static ConcurrentLinkedQueue<Connection> freeConnections = new ConcurrentLinkedQueue<Connection>();
	private static AtomicInteger createdConnections = new AtomicInteger(0);
	
	/**
	 * 
	 * @return
	 */
	public static Connection getConnection() {
		
		Connection con = freeConnections.poll();
		
		if(con != null && isConnectionValid(con)){
			return con;
		}else if(canCreateConnection()){
			con = newConnection();
		}else{
			Log.error(ConnectionManager.class, "Attempting to get a connection is failed ");
		}
		
		return con;
	}
	
	private static boolean isConnectionValid(Connection con){
		
		try{
			pingConnection(con);
		}catch(Exception ex){
			Log.info(ConnectionManager.class, "XXXXXXXXX :: Connection Ping Faild - Creating new Connection !");
			releaseConnection(con);
			return false;
		}
		
		
		return true;
	}
	
	private static void pingConnection(Connection con) throws SQLException{
		String sql = "/* ping */ SELECT 1";
		Statement stmnt = con.createStatement();
		stmnt.execute(sql);
	}
	
	private static boolean canCreateConnection() {
		
		boolean result = false;
		String poolSize = Configs.getConfigValue(Constants.Configs.CONNECTIONS_POOL_SIZE);
		int maxConnections = Integer.valueOf(poolSize);
				
		if (maxConnections == 0 || maxConnections > createdConnections.intValue()) {
			result = true;
		}
		
		return result;
	}
	
	/**
	 * return a connection to the pool
	 */
	public static void freeConnection(Connection con) {

		if (con == null || freeConnections.contains(con)) {
			return;
		}

		freeConnections.add(con);
		
	}
	
	/**
	 * 
	 * @param con
	 */
	public static void releaseConnection(Connection con){
		
		createdConnections.decrementAndGet();
		
		if (con == null) {
			return;
		}
		
		if(freeConnections.contains(con)){
			freeConnections.remove(con);
		}
		
		try {
			con.close();
		} catch (SQLException e) {
			Log.info(ConnectionManager.class, "XXXXXXXXX :: Connection Close Faild !");
		}
		
	}
	
	/**
	 * release all connections and empty pool
	 */
	public static synchronized void releaseAllConnections() {

		for (Iterator<Connection> allConnections = freeConnections.iterator(); allConnections.hasNext();) {
			Connection con = allConnections.next();
			try {
				con.close();
				createdConnections.decrementAndGet();
			} catch (Exception ex) {
				Log.error(ConnectionManager.class, "Error closing a connection ", ex);
			}
		}
		freeConnections.clear();
	}
	
	/**
	 * 
	 * @return Connection
	 */
	private static Connection newConnection() {
		
		String DB_CONN_STRING = Configs.getConfigValue(Constants.Configs.DB_CONN_STRING);
		String DRIVER_CLASS_NAME = Configs.getConfigValue(Constants.Configs.DRIVER_CLASS_NAME);
		String USER_NAME = Configs.getConfigValue(Constants.Configs.USER_NAME);
		String PASSWORD = Configs.getConfigValue(Constants.Configs.PASSWORD);

		Connection result = null;
		try {
			Class.forName(DRIVER_CLASS_NAME);
			result = DriverManager.getConnection(DB_CONN_STRING, USER_NAME, PASSWORD);
			createdConnections.incrementAndGet();
			
		} catch (Exception ex) {
			ex.printStackTrace();
			Log.error(ConnectionManager.class, "Error creating a connection ", ex);
		}
		return result;
	}
	
}
